﻿using System;
using System.ComponentModel;
using System.Linq;
using ProjectEuler.Extensions;
using ProjectEuler.Linq;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;

namespace ProjectEuler.Problems
{
    [EulerProblem(59)]
    [Description("Using a brute force attack, can you decrypt the cipher using XOR encryption?")]
    internal sealed class Problem059 : EulerProblem
    {
        private static readonly Byte[] LettersByFrequency = Encoding.ASCII.GetBytes("eEtTaAoOnNrRiIsShH");

        public override Object Solve()
        {
            var bytes = ReadBinaryAttachment();

            var byKey = new[]
            {
                 new List<Byte>(bytes.Length / 3),
                 new List<Byte>(bytes.Length / 3),
                 new List<Byte>(bytes.Length / 3),
            };

            for (int i = 0; i < bytes.Length; i++)
            {
                byKey[i % byKey.Length].Add(bytes[i]);
            }

            foreach (var charSet in byKey)
            {
                var counts = (from g in bytes.GroupBy(b => b)
                              let count = g.Count()
                              orderby count descending
                              select new
                              {
                                  Byte = g.Key,
                                  Count = count,
                              }).ToArray();

                var key = (Byte)(0);
                var f = 0;

                while ((key == 0) && (f < counts.Length))
                {
                    key = TryFindLowerCaseKey(counts[f].Byte);
                    f++;
                }

                Debug.Write((Char)(key));

                for (int i = 0; i < charSet.Count; i++)
                {
                    charSet[i] ^= key;
                }
            }

            var result = new Byte[bytes.Length];

            for (int i = 0; i < result.Length; i++)
            {
                result[i] = byKey[i % byKey.Length][i / byKey.Length];
            }

            return null;
        }

        private Byte TryFindLowerCaseKey(Byte b)
        {
            foreach (var l in LettersByFrequency)
            {
                var candidate = (Byte)(b ^ l);

                if (Char.IsLower((Char)(candidate)))
                {
                    return candidate;
                }
            }

            return 0;
        }
    }
}
